﻿namespace MtmDeepCopy
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Net;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Data;
    using System.Windows.Threading;
    using System.Xml;
    using System.Xml.Linq;
    using Microsoft.TeamFoundation.Client;
    using Microsoft.TeamFoundation.TestManagement.Client;
    using Microsoft.TeamFoundation.WorkItemTracking.Client;
    using Sogeti.QueryWrapper;
    using Sogeti.VSExtention;

    public delegate void ActionNotifyCallbackDelegate(double value, string currentOperation);

    public delegate void UpdateNodesDelegate(string guid, int id);

    public interface IAction
    {
        string Title { get;  }

        bool Execute(ref Dictionary<string, object> sharedVariables, ActionNotifyCallbackDelegate notifyCallback, UpdateNodesDelegate nodeUpdateCallback);
    }

    public class NewStaticSuiteAction : IAction
    {
        private string targetName;
        private XmlNode node;
        private XmlNode nodeParent;

        public NewStaticSuiteAction(XmlNode n, XmlNode nParent, string name)
        {
            node = n;
            nodeParent = nParent;

            targetName = name;
        }

        string IAction.Title 
        { 
            get 
            { 
                return "Create a new static Suite " + targetName + " to parent " + targetName; 
            } 
        }

        bool IAction.Execute(ref Dictionary<string, object> sharedVariables, ActionNotifyCallbackDelegate notifyCallback, UpdateNodesDelegate nodeUpdateCallback)
        {
            try
            {
                ITestManagementTeamProject tpSource = GetSharedVariable<ITestManagementTeamProject>("SourceTestTeamProject", sharedVariables);
                ITestManagementTeamProject tpTarget = GetSharedVariable<ITestManagementTeamProject>("TargetTestTeamProject", sharedVariables);

                int targetId = System.Convert.ToInt16(nodeParent.Attributes["SuiteId"].Value);

                IStaticTestSuite target = (IStaticTestSuite)tpTarget.TestSuites.Find(targetId);

                IStaticTestSuite statNew = tpTarget.TestSuites.CreateStatic();
                statNew.Title = targetName;
                target.Entries.Add(statNew);

                notifyCallback(0.90, "Saving plan");
                target.Plan.Save();

                nodeUpdateCallback(node.Attributes["Guid"].Value.ToString(), statNew.Id);

                return true;
            }
            catch (Exception ex)
            {
                notifyCallback(0.99, "Unexpected error " + ex.Message);
                return false;
            }
        }

        public T GetSharedVariable<T>(string name, Dictionary<string, object> sharedVariables)
        {
            object o;
            sharedVariables.TryGetValue(name, out o);
            return (T)o;
        }
    }

    public class RenameSuiteAction : IAction
    {
        private XmlNode node;
        
        public RenameSuiteAction(XmlNode n)
        {
            node = n;
        }

        string IAction.Title 
        { 
            get 
            { 
                return "Rename a Suite to " + node.Attributes["Name"].Value.ToString(); 
            } 
        }

        bool IAction.Execute(ref Dictionary<string, object> sharedVariables, ActionNotifyCallbackDelegate notifyCallback, UpdateNodesDelegate nodeUpdateCallback)
        {
            try
            {
                ITestManagementTeamProject tpTarget = GetSharedVariable<ITestManagementTeamProject>("TargetTestTeamProject", sharedVariables);

                int targetId = System.Convert.ToInt16(node.Attributes["SuiteId"].Value);
                IStaticTestSuite target = (IStaticTestSuite)tpTarget.TestSuites.Find(targetId);
                target.Title = node.Attributes["Name"].Value.ToString();
                
                notifyCallback(0.90, "Saving plan");
                target.Plan.Save();

                nodeUpdateCallback(node.Attributes["Guid"].Value.ToString(), targetId);

                return true;
            }
            catch (Exception ex)
            {
                notifyCallback(0.99, "Unexpected error " + ex.Message);
                return false;
            }
        }

        public T GetSharedVariable<T>(string name, Dictionary<string, object> sharedVariables)
        {
            object o;
            sharedVariables.TryGetValue(name, out o);
            return (T)o;
        }
    }

    public class DeepCopySuiteAction : IAction
    {
        public int SourceId;
        public string SourceName;
        
        public string targetName;
        private XmlNode node;
        private XmlNode nodeParent;
        private List<NameValuePair> lstOverrideField;
        private bool copyReq;

        public DeepCopySuiteAction(XmlNode n, XmlNode nParent, bool cloneReq, List<NameValuePair> overrideFields)
        {
            node = n;
            nodeParent = nParent;

            lstOverrideField = overrideFields;
            
            copyReq = cloneReq;

            SourceId = System.Convert.ToInt32(node.Attributes["SourceSuiteId"].Value);
            SourceName = node.Attributes["Name"].Value.ToString();
            
            targetName = nParent.Attributes["Name"].Value;
        }

        string IAction.Title { get { return "Deep copy suite " + SourceId + " " + SourceName + " to parent " + targetName; } }

        bool IAction.Execute(ref Dictionary<string, object> sharedVariables, ActionNotifyCallbackDelegate notifyCallback, UpdateNodesDelegate nodeUpdateCallback)
        {
            try
            {
                ITestManagementTeamProject tpSource = GetSharedVariable<ITestManagementTeamProject>("SourceTestTeamProject", sharedVariables);
                ITestManagementTeamProject tpTarget = GetSharedVariable<ITestManagementTeamProject>("TargetTestTeamProject", sharedVariables);
                ITestSuiteBase source = tpSource.TestSuites.Find(SourceId);
                
                int targetId = System.Convert.ToInt16(nodeParent.Attributes["SuiteId"].Value);
                
                IStaticTestSuite target = (IStaticTestSuite)tpTarget.TestSuites.Find(targetId);

                CloneOptions co = new CloneOptions();
                co.CloneRequirements = copyReq;
                if (lstOverrideField.Count > 0)
                {
                    co.OverrideFieldDetails = lstOverrideField.ToArray();
                }

                int opId = tpSource.TestSuites.BeginCloneOperation(source, target, co);
                
                CloneOperationInformation coInfo;
                do
                {
                    Thread.Sleep(1500);
                    coInfo = tpSource.TestSuites.GetCloneOperationInformation(opId);
                    if (coInfo.State == CloneOperationState.Queued)
                    {
                        notifyCallback(0.05, "Queued ");
                    }

                    if (coInfo.State == CloneOperationState.InProgress)
                    {
                        double d = coInfo.ClonedTestCaseCount != 0 ? coInfo.ClonedTestCaseCount * 100 / coInfo.TotalTestCaseCount : 0.5;
                        string sMsg = "In progress";
                        if (coInfo.ClonedTestCaseCount != 0)
                        {
                            sMsg += ", Cloned " + coInfo.ClonedTestCaseCount + " of " + coInfo.TotalTestCaseCount;
                        }

                        notifyCallback(d, sMsg);
                    }
                } 
                while (coInfo.State == CloneOperationState.Queued || coInfo.State == CloneOperationState.InProgress);

                if (coInfo.State == CloneOperationState.Failed)
                {
                    notifyCallback(0.99, "Unexpected Error: " + coInfo.Message);
                }

                nodeUpdateCallback(node.Attributes["Guid"].Value.ToString(), coInfo.ResultObjectId);

                return coInfo.State == CloneOperationState.Succeeded;
            }
            catch (Exception ex)
            {
                notifyCallback(0.99, "Unexpected error " + ex.Message);
                return false;
            }
        }

        public T GetSharedVariable<T>(string name, Dictionary<string, object> sharedVariables)
        {
            object o;
            sharedVariables.TryGetValue(name, out o);
            return (T)o;
        }
    }

    public class ShallowCopySuiteAction : IAction
    {
        public string sourceName;
        public string targetName;

        private XmlNode node;
        private XmlNode nodeParent;
        private bool ConvertReqToStatic = false;

        public ShallowCopySuiteAction(XmlNode n, XmlNode nParent, bool convertReq2Static)
        {
            node = n;
            nodeParent = nParent;
            sourceName = node.Attributes["Name"].Value.ToString();
            targetName = nParent.Attributes["Name"].Value;
            ConvertReqToStatic = convertReq2Static;
        }


        string IAction.Title { get { return "Copy suite " + sourceName + " to parent " + targetName; } }

        public T GetSharedVariable<T>(string name, Dictionary<string, object> sharedVariables)
        {
            object o;
            sharedVariables.TryGetValue(name, out o);
            return (T)o;
        }

        bool IAction.Execute(ref Dictionary<string, object> sharedVariables, ActionNotifyCallbackDelegate notifyCallback, UpdateNodesDelegate nodeUpdateCallback)
        {
            try
            {
                notifyCallback(0.01, "Start copying: " + targetName);

                ITestManagementTeamProject tpSource = GetSharedVariable<ITestManagementTeamProject>("SourceTestTeamProject", sharedVariables);
                ITestManagementTeamProject tpTarget = GetSharedVariable<ITestManagementTeamProject>("TargetTestTeamProject", sharedVariables);

                int targetId = System.Convert.ToInt16(nodeParent.Attributes["SuiteId"].Value);

                IStaticTestSuite target = (IStaticTestSuite)tpTarget.TestSuites.Find(targetId);

                notifyCallback(0.10, "Creating " + node.Attributes["Type"].Value + " suite");
                string title = node.Attributes["Name"].Value;
  
                ITestSuiteBase newTS = null;
                switch ((TestSuiteEntryType)Enum.Parse(typeof(TestSuiteEntryType), node.Attributes["Type"].Value))
                {
                    case TestSuiteEntryType.StaticTestSuite:
                        IStaticTestSuite statNew = tpTarget.TestSuites.CreateStatic();
                        statNew.Title = title;
                        target.Entries.Add(statNew);
                        newTS = statNew as ITestSuiteBase;
                        break;
                    case TestSuiteEntryType.DynamicTestSuite:
                        IDynamicTestSuite dynamicSuite = CreateDynamicSuite(tpTarget, title, node.Attributes["QueryText"].Value);
                        target.Entries.Add(dynamicSuite);
                        newTS = dynamicSuite as ITestSuiteBase;
                        break;
                    case TestSuiteEntryType.RequirementTestSuite:
                        if (ConvertReqToStatic)
                        {
                            int reqId = System.Convert.ToInt32(node.Attributes["RequirementId"].Value);
                            IStaticTestSuite newStatSuite = tpTarget.TestSuites.CreateStatic();
                            newStatSuite.Title = node.Attributes["RequirementId"].Value.ToString() + ": " + node.Attributes["Name"].Value.ToString();
                            target.Entries.Add(newStatSuite);
                            newTS = newStatSuite as ITestSuiteBase;
                        }
                        else
                        {
                            int reqId = System.Convert.ToInt32(node.Attributes["RequirementId"].Value);
                            IRequirementTestSuite reqNew = CreateRequirementSuite(tpTarget, reqId, title);
                            target.Entries.Add(reqNew);
                            newTS = reqNew as ITestSuiteBase;
                        }

                        break;
                    case TestSuiteEntryType.TestCase:
                        int tcId = System.Convert.ToInt32(node.Attributes["TestCaseId"].Value);
                        ITestCase tc = tpTarget.TestCases.Find(tcId);
                        target.Entries.Add(tc);
                        
                        break;
                }

                notifyCallback(0.90, "Saving plan");
                target.Plan.Save();

                if (newTS != null)
                {
                    nodeUpdateCallback(node.Attributes["Guid"].Value.ToString(), newTS.Id);
                }

                notifyCallback(0.99, "Done... ");
                return true;
            }
            catch (Exception ex)
            {
                notifyCallback(0.99, "Unexpected error " + ex.Message);
                return false;
            }
        }

        private void SetNode(ref XmlNode node, int id)
        {
            XmlAttribute aSuiteId = node.OwnerDocument.CreateAttribute("SuiteId");
            aSuiteId.Value = id.ToString();
            node.Attributes.Append(aSuiteId);
        }

        private IRequirementTestSuite CreateRequirementSuite(ITestManagementTeamProject tpTarget, int workItemId, string title)
        {
            WorkItem wi = tpTarget.WitProject.Store.GetWorkItem(workItemId);
            IRequirementTestSuite reqSuite = tpTarget.TestSuites.CreateRequirement(wi);
            reqSuite.Title = title;
            return reqSuite;
        }

        private IDynamicTestSuite CreateDynamicSuite(ITestManagementTeamProject tpTarget, string title, string queryWIQL)
        {
            IDynamicTestSuite dynamicSuite = tpTarget.TestSuites.CreateDynamic();
            dynamicSuite.Title = title;
            dynamicSuite.Query = tpTarget.CreateTestQuery(queryWIQL);
            return dynamicSuite;
        }
    }

    public class CopyTestPlanAction : IAction
    {
        public string SourceName { get; set; }

        public string TargetName { get; set; }

        private int SrcTestPlanId { get; set; }

        public CopyTestPlanAction(int sourceTestPlanId, string sourceTestPlanName,  string targetTestPlanName)
        {
            SrcTestPlanId = sourceTestPlanId;
            SourceName = sourceTestPlanName;
            
            TargetName = targetTestPlanName;
        }


        public T GetSharedVariable<T>(string name, Dictionary<string, object> sharedVariables)
        {
            object o;
            sharedVariables.TryGetValue(name, out o);
            return (T)o;
        }

        string IAction.Title 
        { 
            get 
            { 
                return "Copy tesplan " + SourceName + " to " + TargetName; 
            } 
        }

        bool IAction.Execute(ref Dictionary<string, object> sharedVariables, ActionNotifyCallbackDelegate notifyCallback, UpdateNodesDelegate nodeUpdateCallback)
        {
            try
            {
                notifyCallback(0.01, "Start copying: " + SourceName);

                ITestManagementTeamProject tpSource = GetSharedVariable<ITestManagementTeamProject>("SourceTestTeamProject", sharedVariables);
                ITestManagementTeamProject tpTarget = GetSharedVariable<ITestManagementTeamProject>("TargetTestTeamProject", sharedVariables);

                ITestPlan sourceTestPlan = tpSource.TestPlans.Find(SrcTestPlanId);
                ITestPlan targetTestPlan = sourceTestPlan.Clone();
                targetTestPlan.Name = TargetName;

                notifyCallback(0.90, "Saving plan");
                targetTestPlan.Save();

                notifyCallback(0.99, "Done... ");
                return true;
            }
            catch (Exception ex)
            {
                notifyCallback(0.99, "Unexpected error " + ex.Message);
                return false;
            }
        }
    }

    public class StaticSuiteWithTCfromQueryAction : IAction
    {
        //Create a suite with all test cases in a query
        private string suiteName;
        private Guid queryId;
        private string queryPath;

        private System.Xml.XmlElement parentSuiteNode;

        public StaticSuiteWithTCfromQueryAction(string suiteName, Guid theQueryId, string path, System.Xml.XmlElement parentSuiteNode)
        {
            // TODO: Complete member initialization
            this.suiteName = suiteName;
            this.queryId = theQueryId;
            this.queryPath = path;
            this.parentSuiteNode = parentSuiteNode;
        }

        string IAction.Title { get { return string.Format("Create a suite {0} with all test cases in query {1}", suiteName, queryPath); } }

        bool IAction.Execute(ref Dictionary<string, object> sharedVariables, ActionNotifyCallbackDelegate notifyCallback, UpdateNodesDelegate nodeUpdateCallback)
        {
            try
            {
                notifyCallback(0.01, "Start adding Test cases from linked query : " + queryPath);

                ITestManagementTeamProject tpSource = GetSharedVariable<ITestManagementTeamProject>("SourceTestTeamProject", sharedVariables);
                ITestManagementTeamProject tpTarget = GetSharedVariable<ITestManagementTeamProject>("TargetTestTeamProject", sharedVariables);
                TeamExplorerIntergator te = GetSharedVariable<TeamExplorerIntergator>("TeamExplorerIntegrator", sharedVariables);

                notifyCallback(0.05, "Fetching parent suite");

                int parentId = System.Convert.ToInt16(parentSuiteNode.Attributes["SuiteId"].Value);
                IStaticTestSuite parentSuite = (IStaticTestSuite)tpTarget.TestSuites.Find(parentId);

                notifyCallback(0.10, "Creating static suite");

                IStaticTestSuite statNew = tpTarget.TestSuites.CreateStatic();
                statNew.Title = suiteName;
                parentSuite.Entries.Add(statNew);
                
                notifyCallback(0.15, "Executing query");
                TPQueryWrapper qw = new TPQueryWrapper(te);
                
                List<WorkItem> lst = qw.SelectWorkItemsFromQuery("Test Case", queryId);

                foreach (WorkItem wi in qw.SelectDistinct(lst))
                {
                    ITestCase tc = tpTarget.TestCases.Find(wi.Id);
                    statNew.Entries.Add(tc);
                }

                notifyCallback(0.90, "Saving plan");
                parentSuite.Plan.Save();

                notifyCallback(0.99, "Done... ");
                return true;
            }
            catch (Exception ex)
            {
                notifyCallback(0.99, "Unexpected error " + ex.Message);
                return false;
            }
        }

        public T GetSharedVariable<T>(string name, Dictionary<string, object> sharedVariables)
        {
            object o;
            sharedVariables.TryGetValue(name, out o);
            return (T)o;
        }
    }
}
